{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# SSL Connection Examples"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Connecting to a Redis instance via SSL"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import redis\n",
    "\n",
    "r = redis.Redis(\n",
    "    host='localhost',\n",
    "    port=6666,\n",
    "    ssl=True,\n",
    "    ssl_cert_reqs=\"none\",\n",
    ")\n",
    "r.ping()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Connecting to a Redis instance via a URL string"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import redis\n",
    "\n",
    "r = redis.from_url(\"rediss://localhost:6666?ssl_cert_reqs=none&decode_responses=True&health_check_interval=2\")\n",
    "r.ping()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Connecting to a Redis instance using a ConnectionPool"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import redis\n",
    "\n",
    "redis_pool = redis.ConnectionPool(\n",
    "    host=\"localhost\",\n",
    "    port=6666,\n",
    "    connection_class=redis.SSLConnection,\n",
    "    ssl_cert_reqs=\"none\",\n",
    ")\n",
    "\n",
    "r = redis.StrictRedis(connection_pool=redis_pool)\n",
    "r.ping()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Connecting to a Redis instance via SSL, while specifying a minimum TLS version"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import redis\n",
    "import ssl\n",
    "\n",
    "r = redis.Redis(\n",
    "    host=\"localhost\",\n",
    "    port=6666,\n",
    "    ssl=True,\n",
    "    ssl_min_version=ssl.TLSVersion.TLSv1_3,\n",
    "    ssl_cert_reqs=\"none\",\n",
    ")\n",
    "r.ping()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Connecting to a Redis instance via SSL, while specifying a self-signed SSL CA certificate"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import os\n",
    "import redis\n",
    "\n",
    "pki_dir = os.path.join(\"..\", \"..\", \"dockers\", \"stunnel\", \"keys\")\n",
    "\n",
    "r = redis.Redis(\n",
    "    host=\"localhost\",\n",
    "    port=6666,\n",
    "    ssl=True,\n",
    "    ssl_certfile=os.path.join(pki_dir, \"client-cert.pem\"),\n",
    "    ssl_keyfile=os.path.join(pki_dir, \"client-key.pem\"),\n",
    "    ssl_cert_reqs=\"required\",\n",
    "    ssl_ca_certs=os.path.join(pki_dir, \"ca-cert.pem\"),\n",
    ")\n",
    "r.ping()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Connecting to a Redis instance via SSL, and validate the OCSP status of the certificate\n",
    "\n",
    "The redis package is designed to be small, meaning extra libraries must be installed, in order to support OCSP stapling. As a result, first install redis via:\n",
    "\n",
    "`pip install redis[ocsp]`\n",
    "\n",
    "This will install cryptography, requests, and PyOpenSSL, none of which are generally required to use Redis.\n",
    "\n",
    "In the next example, we will connect to a Redis instance via SSL, and validate the OCSP status of the certificate. However, the certificate we are using does not have an AIA extension, which means that the OCSP validation cannot be performed."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "OCSP validation failed as expected.\n"
     ]
    }
   ],
   "source": [
    "import os\n",
    "import redis\n",
    "\n",
    "pki_dir = os.path.join(\"..\", \"..\", \"dockers\", \"stunnel\", \"keys\")\n",
    "\n",
    "r = redis.Redis(\n",
    "    host=\"localhost\",\n",
    "    port=6666,\n",
    "    ssl=True,\n",
    "    ssl_certfile=os.path.join(pki_dir, \"client-cert.pem\"),\n",
    "    ssl_keyfile=os.path.join(pki_dir, \"client-key.pem\"),\n",
    "    ssl_cert_reqs=\"required\",\n",
    "    ssl_ca_certs=os.path.join(pki_dir, \"ca-cert.pem\"),\n",
    "    ssl_validate_ocsp=True,\n",
    ")\n",
    "\n",
    "try:\n",
    "    r.ping()\n",
    "except redis.ConnectionError as e:\n",
    "    assert e.args[0] == \"No AIA information present in ssl certificate\"\n",
    "    print(\"OCSP validation failed as expected.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Connect to a Redis instance via SSL, and validate OCSP-stapled certificates\n",
    "\n",
    "It is also possible to validate an OCSP stapled response. Again, for this example the server does not send an OCSP stapled response, so the validation will fail."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "OCSP validation failed as expected.\n"
     ]
    }
   ],
   "source": [
    "import os\n",
    "import redis\n",
    "\n",
    "pki_dir = os.path.join(\"..\", \"..\", \"dockers\", \"stunnel\", \"keys\")\n",
    "ca_cert = os.path.join(pki_dir, \"ca-cert.pem\")\n",
    "\n",
    "# It is possible to specify an expected certificate, or leave it out.\n",
    "expected_certificate = open(ca_cert, 'rb').read()\n",
    "\n",
    "# If needed, a custom SSL context for OCSP can be specified via ssl_ocsp_context\n",
    "\n",
    "r = redis.Redis(\n",
    "    host=\"localhost\",\n",
    "    port=6666,\n",
    "    ssl=True,\n",
    "    ssl_certfile=os.path.join(pki_dir, \"client-cert.pem\"),\n",
    "    ssl_keyfile=os.path.join(pki_dir, \"client-key.pem\"),\n",
    "    ssl_cert_reqs=\"required\",\n",
    "    ssl_ca_certs=ca_cert,\n",
    "    ssl_validate_ocsp_stapled=True,\n",
    "    ssl_ocsp_expected_cert=expected_certificate,\n",
    ")\n",
    "\n",
    "try:\n",
    "    r.ping()\n",
    "except redis.ConnectionError as e:\n",
    "    assert e.args[0] == \"no ocsp response present\"\n",
    "    print(\"OCSP validation failed as expected.\")"
   ]
  }
 ],
 "metadata": {
  "interpreter": {
   "hash": "d45c99ba0feda92868abafa8257cbb4709c97f1a0b5dc62bbeebdf89d4fad7fe"
  },
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
